using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
using System.Windows;
using System.Diagnostics.Contracts;

namespace AppToolkit.WPF.UI.Converters
{
    /// <summary>
    /// Converts <see cref="bool"/> value to a <see cref="Visibility"/> value.
    /// </summary>
    [ValueConversion(typeof(bool), typeof(Visibility))]
    public class BoolToVisiblityConverter : IValueConverter
    {
        private Visibility _HiddenValue = Visibility.Collapsed;

        #region Instance
        private static BoolToVisiblityConverter _instance = new BoolToVisiblityConverter();
        
        /// <summary>
        /// Singleton for sharing Converter across controls (<see cref="FolderBrowser"/>).
        /// </summary>
        public static BoolToVisiblityConverter Instance
        {
            get
            {
                if (null == _instance)
                {
                    lock (typeof(BoolToVisiblityConverter))
                    {
                        if (null == _instance)
                        {
                            _instance = new BoolToVisiblityConverter();
                        }
                    }
                }
                return _instance;
            }
        } 
        #endregion

        #region IValueConverter implementation
        /// <summary>
        /// Forward conversion from <see cref="bool"/> to <see cref="Visibility"/>.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is bool))
                return DependencyProperty.UnsetValue;

            var bValue = ((bool)value);

            if (this.InvertLogic)
            {
                bValue = !bValue;
            }

            if (bValue)
            {
                return (Visibility.Visible);
            }
            else
            {
                return this.HiddenValue;
            }
        }

        /// <summary>
        /// <see cref="IValueConverter.ConvertBack"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is Visibility))
                return DependencyProperty.UnsetValue;

            var vValue = ((Visibility)value);
            if (vValue == Visibility.Hidden)
            {
                vValue = Visibility.Collapsed;
            }

            if (this.InvertLogic)
            {
                if (vValue == Visibility.Visible)
                {
                    vValue = Visibility.Collapsed;
                }
                else
                {
                    vValue = Visibility.Visible;
                }
            }
            return (vValue == Visibility.Visible);
        } 
        #endregion

        #region Property Visibility HiddenValue { get;set; }
        /// <summary>
        /// Defines the value that shall be used for hidden. 
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">In case a values not of Visibility.Collapsed/Visibility.Hidden 
        /// is specified.</exception>
        public Visibility HiddenValue
        {
            get
            {
                return _HiddenValue;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value != Visibility.Visible, "Allowed values are: 'Collapsed'(default) and 'Hidden'");
                _HiddenValue = value;
            }
        }
        #endregion

        /// <summary>
        /// Inverts the boolean value before mapping it to Visibility.
        /// </summary>
        public bool InvertLogic { get; set; }
    }
}
